package org.yinxue.swing.unit.model;


import org.yinxue.swing.core.util.StringUtil;
import org.yinxue.swing.unit.constant.ClassConstant;
import org.yinxue.swing.unit.constant.ClassType;
import org.yinxue.swing.unit.constant.MethodType;
import org.yinxue.swing.unit.constant.UnitConstant;
import org.yinxue.swing.unit.context.Context;
import org.yinxue.swing.unit.util.UnitUtil;
import org.yinxue.swing.unit.visitor.Accepter;
import org.yinxue.swing.unit.visitor.MockitoVisitor;
import org.yinxue.swing.unit.visitor.Visitor;
import org.yinxue.swing.unit.visitor.VisitorFactory;

import java.util.*;

/**
 * 类描述信息
 *
 * @author zengjian
 * @create 2018-04-29 9:20
 * @since 1.0.0
 */
public class ClassDesc implements Accepter, ClassConstant {

    /**
     * 标识符
     */
    public int modifier;

    /**
     * 类类型描述
     */
    public ClassType classType;

    /**
     * 所在包行
     */
    public String packageLine;

    /**
     * 导入包行，类名:类路径; "StringUtil":"import...StringUtil;"
     */
    public Map<String, String> sourceImportLines = new TreeMap<>();

    /**
     * 类header开始行
     */
    public String classStartLine;

    /**
     * 类header内容，开始到左大括号"{"
     */
    public String classHeader;

    /**
     * 类名(不含包路径)
     */
    public String simpleName;

    /**
     * 组合名称，普通类=simpleName，内部类= parent.simpleName+"." this.simpleName
     */
    public String compositClassName;

    /**
     * 全路径名称  com.xxx.com.xxx.MainClass
     */
    public String className;

    /**
     * 采用的默认变量，比如首字母小写
     */
    public String defaultVariable;

    /**
     * 包路径(不含package关键字及结束符;)
     */
    public String packagePath;

    /**
     * 导入路径，import + className + ;
     */
    public String importPath;

    /**
     * 文本行数组
     */
    public String[] lines;

    /**
     * 类全文
     */
    public String context;

    /**
     * 扫描起始行，内部类起始行
     */
    public Integer start = 0;

    /**
     * 行扫描游标
     */
    public Integer cursor = start;

    /**
     * 内部类结束行
     */
    public Integer end;

    /**
     * 类中：类扫描跳过的行，如，内部类，private方法，protected方法
     */
    public List<Integer> noScanClassLines = new ArrayList<>(256);

    /**
     * 如果扫描到的下一个方法，在上一个方法的区间，那么这个方法是方法体内的，所以需要跳过，适用于匿名内部类的情况
     */
    public List<Integer> noScanMethodLines = new ArrayList<>(256);

    /**
     * 父类引用，simpleName:classDesc classDesc刷新后注入
     */
    public Map<String, ClassDesc> superClassRef = new LinkedHashMap<>(16);

    /**
     * 实例变量
     */
    public Map<String, FieldDesc> fieldMap = new LinkedHashMap<>(16);

    /**
     * 实例方法
     */
    public Map<String, MethodDesc> methodMap = new LinkedHashMap<>(32);

    /**
     * 构造方法，没查找到会默认保存一个默认的无参构造
     */
    public Map<String, MethodDesc> constructrorMap = new TreeMap<String, MethodDesc>(new Comparator<String>() {
        /**
         * 构造器依次从无参(key短)到(key长)有参排，避免命名重复，用length来判断
         * @param o1
         * @param o2
         * @return
         */
        @Override
        public int compare(String o1, String o2) {
            return o1.length()-o2.length();
        }
    });

    /**
     * 内部类
     */
    public List<ClassDesc> nestClassList = new LinkedList<>();

    /**
     * 继承父类时泛型，key 类名称  泛型类
     */
    public Map<String, List<String>> genericType = new LinkedHashMap<>();

    /**
     * 枚举类型时，枚举列表
     */
    public List<String> enumList = new LinkedList<>();

    /**
     * 内部类指定的外部类
     */
    public ClassDesc parent;

    /**
     * 生成的单元测试文本
     */
    public String unitContext;

    /**
     * 临时线程变量
     */
    public Visitor.ClassUnitContext tempUnitContext;

    /**
     * java文本转化为数组
     *
     * @param context 入参java文本
     */
    public ClassDesc(String context) {
        this(context, null, null, null, null);
    }

    public ClassDesc(String[] lines) {
        this(null, lines, null, null, null);
    }

    public ClassDesc(String[] lines, ClassDesc parent) {
        this(null, lines, parent, null, null);
    }

    /**
     * 内部类 <br>
     * 增加一个组合名称 {@link #compositClassName} <br>
     *
     * @param lines 父类数组
     * @param parent 嵌套类的父类
     * @param start 起始行
     * @param end 结束行
     */
    public ClassDesc(String[] lines, ClassDesc parent, int start, int end) {
        this(null, lines, parent, start, end);
        this.compositClassName = parent.simpleName + "." + this.simpleName;
    }

    private ClassDesc(String context, String[] lines, ClassDesc parent, Integer start, Integer end) {
        checkContextAndArray(context, lines);
        initParentAndIndex(parent, start, end);
        initClassDesc();
    }

    private void checkContextAndArray(String context, String[] lines) {
        if (StringUtil.isEmpty(context) && lines == null) {
            throw new RuntimeException("context 和 lines不能同时为空");
        }
        if (StringUtil.isEmpty(context) && lines != null) {
            this.context = UnitUtil.parseContext(lines);
            this.lines = lines;
            return;
        }
        if (StringUtil.isNotEmpty(context) && lines == null) {
            // notice：执行了trim操作
            this.lines = UnitUtil.parseArrayAndTrim(context);
            this.context = context;
            return;
        }
        this.context = context;
        this.lines = lines;
    }

    private void initParentAndIndex(ClassDesc parent, Integer start, Integer end) {
        this.parent = parent == null ? null : parent;
        this.start = start == null ? null : start;
        this.end = end == null ? null : end;
    }

    private void initClassDesc() {
        // 内部类不需要分析packageLine和ImportLines
        if (this.parent == null) {
            initPackageLine();
            initImportLine();
        }
        initClassName();
        initClassType();
        initNestClass();
        initPrivateFields();
        initPublicMethod();
        initModifier();
        // 初始化无参构造器
        initConstrutor();
        // 存储到容器
        Context.register().register(this);
    }

    private void initConstrutor() {
        // 如果构造器map中没有构造器，默认给一个无参构造
        if (constructrorMap.isEmpty() && (modifier & GENERIC) != 0) {
            String construtorHeader = "public " + this.compositClassName + "() {";
            constructrorMap.put(construtorHeader, new MethodDesc(construtorHeader, construtorHeader, construtorHeader + "\n}", this, MethodType.CONSTRUCTOR));
        }
    }

    private void initPackageLine() {
        for (int i = 0; i < this.lines.length; i++) {
            if (this.lines[i].startsWith("package")) {
                this.packageLine = this.lines[i];
                this.packagePath = this.packageLine.replaceAll("(import|;|package)", "").trim();
                this.cursor = i;
                break;
            }
        }
    }

    private void initImportLine() {
        for (int i = this.cursor, length = this.lines.length; i < length; i++) {
            if (this.lines[i].startsWith(("import"))) {
                // 这里可能出现*(或者小概率重复SimpleName)的情况，需要进去重复判断，在原名称上加下划线
                String simpleName = findImportClassName(this.lines[i]);
                if (this.sourceImportLines.containsKey(simpleName)) {
                    simpleName += "_" + UUID.randomUUID().toString();
                }
                this.sourceImportLines.put(simpleName, this.lines[i]);
            } else if (this.lines[i].matches("(public |abstract |static |class |final |interface |enum |@interface )[\\s\\S]*")) {
                this.cursor = i;
                break;
            } else {
                // 会到此处吗？ doNothing;
            }
        }
    }

    static String findImportClassName(String line) {
        return line.substring(line.lastIndexOf(".") + 1, line.lastIndexOf(";"));
    }

    static String findImportPath(String line) {
        return line.substring(line.indexOf("import") + "import".length(), line.lastIndexOf(";")).trim();
    }

    private void initClassName() {
        this.classStartLine = this.lines[this.cursor];
        this.simpleName = findClassName(this.classStartLine);
        this.defaultVariable = StringUtil.toLowerCaseFirstChar(this.simpleName);
        this.classHeader = UnitUtil.findBlockByLine(this.cursor, this.classStartLine, "{", this.lines);
        this.className = this.packagePath + "." + this.simpleName;
        this.importPath = "import " + this.className + ";";
        // 取得空引用父类
        // TODO
        this.superClassRef = UnitUtil.resolveSuperClass(this.classHeader, this);
        // 普通类，组合名称=简单名称，内部类后面重置为parent.simpleName+nest.simpleName;
        this.compositClassName = this.simpleName;
    }

    /**
     * 根据获得的classLine，截取到className
     *
     * @param classLine
     * @return
     */
    static String findClassName(String classLine) {
        return classLine.replaceAll(UnitConstant.RETAIN_CLASS_NAME, " ").trim().split(" ")[0];
    }

    private void initClassType() {
        if (classStartLine.contains(" abstract ")) {
            modifier += ABSTRACT;
            classType = ClassType.ABSTRACT;
        } else if (classStartLine.contains(" interface ")) {
            modifier += INTERFACE;
            classType = ClassType.INTERFACE;
        } else if (classStartLine.contains(" enum ")) {
            modifier += ENUM;
            classType = ClassType.ENUM;
        } else if (classStartLine.contains(" @interface ")) {
            modifier += ANNOTATION;
            classType = ClassType.ANNOTATION;
        } else {
            modifier += GENERIC;
            classType = ClassType.GENERIC;
        }
    }

    private void initPrivateFields() {
        for (int i = this.cursor + 1; i < this.lines.length; i++) {
            if (this.noScanClassLines.contains(i)) {
                continue;
            }
            // 先查找注解，如果有注解符合，在注解的下一行一般为私有变量
            // 存在多重注解情况，还存在注解后有空行的情况(不规范写法)
            //    @Autowired
            //    @Qualifier("dalClient")
            //    private JDBCCommand dalClient;
            // 匹配以括号内的内容开头的行，^是表示开始而非否
            if (this.lines[i].matches("^(@Resource|@Autowired)[^=]*")) {
                for (int j = i + 1; j < this.lines.length; j++) {
                    //
                    if (this.lines[j].startsWith("@") || "".equals(this.lines[j])) {
                        continue;
                    } else {
                        FieldDesc fieldDesc = new FieldDesc(this.lines[j], this.lines[i]);
                        fieldMap.put(fieldDesc.varName, fieldDesc);
                        break;
                    }
                }
                continue;
            }

            if (this.lines[i].matches("(private|protected)[ <>a-zA-Z=0-9\\[\\]{},\"\\(\\)]+;")) {
                String annotation = null;
                // 如果前一行为注解类，作为value存储
                if (lines[i - 1].startsWith("@")) {
                    annotation = lines[i - 1];
                }

                // 如果是以下解析类注解直接忽略掉，避免mock
                if (lines[i - 1].contains("static final")
                        || lines[i - 1].contains("@XmlElement")
                        || lines[i - 1].contains("@XStreamAlias")) {
                    annotation = null;
                }

                // 排除常量 static final
                if (lines[i].contains("final static")) {
                    continue;
                }
                FieldDesc fieldDesc = new FieldDesc(this.lines[i], annotation);
                fieldMap.put(fieldDesc.varName, fieldDesc);
            }
        }
    }

    /**
     * 将i-->end的坐标记录在 {@link #noScanClassLines}
     *
     * @see #resolveMethodForGeneric() 解析时避开该坐标区间
     */
    private void initNestClass() {
        for (int i = this.cursor + 1; i < lines.length; i++) {
            if (this.lines[i].matches(UnitConstant.INNER_CLASSES_MATCH)) {
                int end = UnitUtil.getEndLineIndex(i, this.lines);
                for (int j = i; j <= end; j++) {
                    this.noScanClassLines.add(j);
                }
                String[] lines = Arrays.copyOfRange(this.lines, i, end + 1);
                this.nestClassList.add(new ClassDesc(lines, this, i, end));
            }
        }
    }

    private void initPublicMethod() {
        // 接口类对方法进行记录
        // TODO 方法存在两行的情况，需要修改为正则匹配
        // 先匹配出大概的方法，然后在方法中细分查找结束符
        //if (ClassType.INTERFACE == this.classType) {
        if ((this.modifier & INTERFACE) != 0) {
            for (int i = this.cursor; i < this.lines.length; i++) {
                // 未)毕业的方法匹配
                if (this.lines[i].matches("(public )?[a-zA-Z0-9<>, \\[\\]]+ {1,3}[a-zA-Z0-9<>\\[\\]]+\\([\\s\\S]*")) {
                    if (this.lines[i].matches("(public )?[a-zA-Z0-9<>, \\[\\]]+ {1,3}[a-zA-Z0-9<>\\[\\]]+\\([\\s\\S]*\\)[\\s\\S]*;")) {
                        this.methodMap.put(this.lines[i], new MethodDesc(this.lines[i], this.lines[i], this.lines[i], this, MethodType.INTERFACE));
                        // TODO 其他情况，寻找下一行,下一行如果符合剩余部分，即进行组合再存储为方法
                    } else {
                        for (int j = i + 1; j < this.lines.length; j++) {
                            if (this.lines[j].matches("^[a-zA-Z0-9_ \\[\\]<>,]+[^=]*;")) {
                                String[] methodLines = Arrays.copyOfRange(this.lines, i, j + 1);
                                StringBuilder builder = new StringBuilder();
                                for (String methodLine : methodLines) {
                                    builder.append(methodLine).append(" ");
                                }
                                String methodPharse = builder.toString().trim();
                                this.methodMap.put(this.lines[i], new MethodDesc(this.lines[i], methodPharse, methodPharse, this, MethodType.INTERFACE));
                                break;
                            }
                        }
                    }
                }
            }
        } else if ((this.modifier & ABSTRACT) != 0) {

        } else if ((this.modifier & ENUM) != 0) {
            // 粗粒度的匹配，无法匹配那种不带参数的枚举
            // 过滤，逐行分析匹配的行，然后截取 开头到第一个(的部分存储到constant中
            initEnumFieldList();
            resolveMethodForGeneric();
        } else if ((this.modifier & ANNOTATION) != 0) {

        } else if ((this.modifier & GENERIC) != 0) {
            resolveMethodForGeneric();
        } else {
            System.out.println("-----no classType------ ");
        }
    }

    private void initEnumFieldList() {
        for (int i = this.cursor; i < this.lines.length; i++) {
            if (this.lines[i].matches("[A-Za-z0-9_]+\\([\\s\\S]+\\)(,|;)")) {
                this.enumList.add(this.simpleName + "." + this.lines[i].substring(0, this.lines[i].lastIndexOf("(")).trim());
            }
        }
    }

    /**
     * 存储到匹配到的方法区间，在下一次匹配到方法时，如果包含就不予以保存。从i+1行开始记录
     */
    private void resolveMethodForGeneric() {
        for (int i = this.cursor + 1; i < this.lines.length; i++) {
            if (this.noScanClassLines.contains(i)) {
                continue;
            }
            // protected private 私有方法的public方法先排除
            if (this.lines[i].matches("(private|protected) [^=]+\\([\\S\\s]*")) {
                if (this.lines[i].contains(this.simpleName + "(")) {
                    // 检查到私有或者protected构造方法，doNothing
                } else {
                    // TODO 有实际需求时再启用
                }
                int end = UnitUtil.getEndLineIndex(i, this.lines);
                for (int j = 0; j <= end; j++) {
                    noScanClassLines.add(j);
                }
            }

            // 匹配public方法
            if (this.lines[i].matches("public [^=]+\\([\\S\\s]*")) {
                // public构造方法
                if (this.lines[i].contains(this.simpleName + "(")) {
                    int end = UnitUtil.getEndLineIndex(i, this.lines);
                    String[] constructorLines = Arrays.copyOfRange(this.lines, i, end + 1);
                    String constructorPharse = UnitUtil.findBlockByLine(0, this.lines[i], "{", constructorLines);
                    this.constructrorMap.put(constructorPharse, new MethodDesc(this.lines[i], constructorPharse, constructorLines, this, MethodType.CONSTRUCTOR));
                    // public普通方法，含static final等修饰符方法，TODO 这里会把内部类的方法也加入，需要设法跳过
                } else if (this.lines[i].contains(" abstract ")) {
                    // 抽象方法不处理
                } else if ("public void run() {".equals(this.lines[i])) {
                    // 线程方法不记录
                } else if (this.lines[i].contains("mapRow(ResultSet")) {
                    // 已知的mapRow方法不记录
                } else {
                    if (this.noScanMethodLines.contains(i)) {
                        continue;
                    }
                    int end = UnitUtil.getEndLineIndex(i, this.lines);
                    String[] methodLines = Arrays.copyOfRange(this.lines, i, end + 1);
                    String methodPharse = UnitUtil.findBlockByLine(0, this.lines[i], "{", methodLines);
                    this.methodMap.put(methodPharse, new MethodDesc(this.lines[i], methodPharse, methodLines, this, MethodType.NEED_MOCK));
                    for (int j = i + 1; j <= end; j++) {
                        this.noScanMethodLines.add(j);
                    }
                }
            }
        }
    }

    private void initModifier() {
        if (this.classStartLine.contains(" static ")) {
            this.modifier += STATIC;
        }
        if (this.classStartLine.contains(" final ")) {
            this.modifier += FINAL;
        }
        if (this.parent != null) {
            this.modifier += NEST;
        }

        if (checkPojo()) {
            this.modifier += POJO;
            if (this.fieldMap.size() < this.methodMap.size()) {
                // 说明除了基本的get set方法还有其他方法
                this.modifier |= POJO_PLUS;
            }
        }

        if (checkAllMethodStatic()) {
            this.modifier += UTIL;
        }
    }

    private boolean checkAllMethodStatic() {
        Collection<MethodDesc> collection = methodMap.values();
        for (MethodDesc methodDesc : collection) {
            if (!((methodDesc.modifier & STATIC) != 0)) {
                return false;
            }
        }
        return true;
    }

    private boolean checkPojo() {
        // 先检查下用户是否有设置，有的话直接采用用户设置
        if (Context.config().isPojo() != false) {
            return Context.config().isPojo();
        }

        // 循环方法，如果其中 get + is 开头的方法 等于 set的方法则表示为pojo类
        int isOrGetCount = 0;
        int setCount = 0;
        for (MethodDesc methodDesc : this.methodMap.values()) {
            if (methodDesc.methodName.startsWith("is") || methodDesc.methodName.startsWith("get")) {
                isOrGetCount++;
            } else if (methodDesc.methodName.startsWith("set")) {
                setCount++;
            }
        }
        if (isOrGetCount != 0 && isOrGetCount == setCount) {
            return true;
        }
        return false;
    }


    /**
     * ----------------------------------------
     * 以下是生成单元测试的方法 <br>
     * ----------------------------------------
     */

    /**
     * 容器操作及返回单元测试文本 <br>
     *
     * @return 单元测试文本
     * @see {@link StatementDesc#addEntryParams(StatementDesc, StringBuilder)}
     * @see {@link ClassDescRegister}
     */
    public String buildUnitTestContext() {
        // 生成单元测试之前进行容器注入，在添加mock的childMethod方式时，还有一次容器的获取
        refreshDependency();
        return accept(VisitorFactory.getInstance(MockitoVisitor.class));
    }

    private void refreshDependency() {
        for (Map.Entry<String, ClassDesc> entry : this.superClassRef.entrySet()) {
            this.superClassRef.put(entry.getKey(), Context.register().getClassDesc(entry.getKey()));
        }
    }

    @Override
    public String accept(Visitor visitor) {
        return visitor.visit(this);
    }

    public boolean hasEnumList() {
        return !enumList.isEmpty();
    }

    public String getDefaultEnum() {
        if (hasEnumList()) {
            return enumList.get(0);
        }
        return "";
    }

    public boolean hasNestClass() {
        return !nestClassList.isEmpty();
    }

    public boolean hasSuperClass() {
        return !superClassRef.isEmpty();
    }

    @Override
    public String toString() {
        // 需要先build下单元测试，获得会mock的类和方法
        buildUnitTestContext();
        StringBuilder builder = new StringBuilder(2048);
        builder.append("解析类名:").append(simpleName).append("\n");
        builder.append("类型:").append(classType.name()).append("\n");
        builder.append("继承或者接口类:");
        for (String s : superClassRef.keySet()) {
            builder.append(s).append(" ");
        }
        builder.append("\n");
        builder.append("解析字段[类型 变量 注解]").append(fieldMap.size()).append("条:\n");
        for (FieldDesc fieldDesc : fieldMap.values()) {
            builder.append(fieldDesc.toString());
        }
        builder.append("--------\n");
        builder.append("解析public方法[返回类型 方法名 入参]").append(methodMap.size()).append("条:\n");
        for (MethodDesc methodDesc : methodMap.values()) {
            builder.append(methodDesc.toString());
        }
        builder.append("--------\n");
        builder.append("解析内部类").append(nestClassList.size()).append("条:\n");
        for (ClassDesc definition : nestClassList) {
            builder.append(definition.toString());
        }
        builder.append("\n");
        return builder.toString();
    }
}
